from datetime import datetime

from bson import ObjectId

from models.common import OID
from utils.modelutil import get_field_type


def convert_value(value, field_type):
    converter = lambda v: v
    if field_type in [OID, ObjectId]:
        converter = lambda v: ObjectId(v)
    elif field_type is datetime:
        converter = lambda v: datetime.fromisoformat(value)
    if isinstance(value, list):
        return [converter(v) for v in value]
    return converter(value)


def load_mongo_query(key, value, field, model):
    """
    :param key:
    :param value:
    :return:
    """
    if key in ["and", "or"]:
        if not isinstance(value, list):
            raise ValueError("and, or operator requires list values")
        query = []
        for elem in value:
            for k, v in elem.items():
                query.append(load_mongo_query(k, v, k, model))
        return {f"${key}": query}
    if key in {'lte', 'gte', 'lt', 'gt', 'regex', 'options', "in", "ne", "nin", "exists"}:
        field_type = get_field_type(model, field)
        return {f"${key}": convert_value(value, field_type)}
    if key == 'elemMatch':
        if not isinstance(value, dict):
            raise ValueError("elemMatch operator requires dict value")
        sub_query = {}
        model = get_field_type(model, field)
        for k, v in value.items():
            sub_query.update(load_mongo_query(k, v, k, model))
        return {"$elemMatch": sub_query}
    query = {}
    if isinstance(value, dict):
        sub_query = {}
        for k, v in value.items():
            sub_query.update(load_mongo_query(k, v, field, model))
        query[key] = sub_query
    elif isinstance(value, (str, int, float, bool)):
        field_type = get_field_type(model, field)
        query[key] = convert_value(value, field_type)
    else:
        raise ValueError(f"key {key} has unsupported value")
    return query


def compose_mongo_query(filter_param: dict, model):
    query = {}
    for k, v in filter_param.items():
        query.update(load_mongo_query(k, v, k, model))
    return query


if __name__ == '__main__':
    from pprint import pprint
    from models.activity import Activity

    filter_param = {
        # "name": {"regex": "加油"},
        "region": {
            "city": {"regex": "加油"}
        }
    }
    pprint(compose_mongo_query(filter_param, Activity))
